﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using System.Reflection;

using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.DataFace;

namespace Noris.Schedule.Support.Data
{
    #region Index obecný (Unique, Multi)
    /// <summary>
    /// Index obecný, unikátní (k jednomu klíči lze evidovat pouze jeden záznam s daty),
    /// postavený nad obecným soupisem dat, z něhož je funkcí vygenerován klíč.
    /// </summary>
    /// <typeparam name="K"></typeparam>
    /// <typeparam name="R"></typeparam>
    public class IndexUnique<K, R> : Dictionary<K, R>
    {
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        public IndexUnique(Func<R, K> indexKeyGenerator)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        public IndexUnique(Func<R, K> indexKeyGenerator, Func<K, bool> indexKeyIgnored)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this._IndexKeyIgnored = indexKeyIgnored;
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        /// <param name="data">Soupis dat, z nichž se index vytvoří</param>
        public IndexUnique(Func<R, K> indexKeyGenerator, IEnumerable<R> data)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this.AddRange(data);
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        /// <param name="data">Soupis dat, z nichž se index vytvoří</param>
        public IndexUnique(Func<R, K> indexKeyGenerator, Func<K, bool> indexKeyIgnored, IEnumerable<R> data)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this._IndexKeyIgnored = indexKeyIgnored;
            this.AddRange(data);
        }
        /// <summary>
        /// Přidá do sebe řadu dalších dat
        /// </summary>
        /// <param name="data"></param>
        public void AddRange(IEnumerable<R> data)
        {
            if (data != null)
            {
                foreach (R item in data)
                    this.Add(item);
            }
        }
        /// <summary>
        /// Přidá do sebe další prvek s daty
        /// </summary>
        /// <param name="item"></param>
        public void Add(R item)
        {
            K key = this._IndexKeyGenerator(item);
            bool ignoreKey = (this._IndexKeyIgnored == null ? false : this._IndexKeyIgnored(key));
            if (!ignoreKey)
            {
                if (this.ContainsKey(key))
                    throw new ArgumentException("IndexUnique: duplicite key " + key.ToString() + " from item " + item.ToString() + "; index now contains " + this.Count.ToString() + " items.");
                
                this.Add(key, item);
            }
        }
        private Func<R, K> _IndexKeyGenerator;
        private Func<K, bool> _IndexKeyIgnored;
    }
    /// <summary>
    /// Index obecný, multipoložkový (k jednomu klíči lze evidovat řadu záznamů, které generují tento klíč),
    /// postavený nad obecným soupisem dat, z něhož je funkcí vygenerován klíč.
    /// </summary>
    /// <typeparam name="K"></typeparam>
    /// <typeparam name="R"></typeparam>
    public class IndexMulti<K, R> : Dictionary<K, List<R>>
    {
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        public IndexMulti(Func<R, K> indexKeyGenerator)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        public IndexMulti(Func<R, K> indexKeyGenerator, Func<K, bool> indexKeyIgnored)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this._IndexKeyIgnored = indexKeyIgnored;
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        /// <param name="data">Soupis dat, z nichž se index vytvoří</param>
        public IndexMulti(Func<R, K> indexKeyGenerator, IEnumerable<R> data)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this.AddRange(data);
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        /// <param name="data">Soupis dat, z nichž se index vytvoří</param>
        public IndexMulti(Func<R, K> indexKeyGenerator, Func<K, bool> indexKeyIgnored, IEnumerable<R> data)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this._IndexKeyIgnored = indexKeyIgnored;
            this.AddRange(data);
        }
        /// <summary>
        /// Přidá do sebe řadu dalších dat
        /// </summary>
        /// <param name="data"></param>
        public void AddRange(IEnumerable<R> data)
        {
            if (data != null)
            {
                foreach (R item in data)
                    this.Add(item);
            }
        }
        /// <summary>
        /// Přidá do sebe jednu další položku.
        /// Pokud klíč z ní vygenerovaný v indexu nemáme, založí se pro klíč další položka do indexu, položka = List obsahující jednotlivé záznamy se shodným klíčem.
        /// Pokud klíč z položky vygenerovaný již v indexu bude, přidá se položka do jeho soupisu.
        /// </summary>
        /// <param name="item"></param>
        public void Add(R item)
        {
            K key = this._IndexKeyGenerator(item);
            List<R> list;
            if (!this.TryGetValue(key, out list))
            {
                list = new List<R>();
                this.Add(key, list);
            }
            list.Add(item);
        }
        private Func<R, K> _IndexKeyGenerator;
        private Func<K, bool> _IndexKeyIgnored;
    }
    #endregion
    #region CLASS IndexDictCls<K, R> : INDEX NAD TABULKOU S DATY (generování indexu, i samotný objekt indexu).
    /// <summary>
	/// Třída IndexDictCls, určená pro generování indexů k datové tabulce.
	/// Index je Dictionary, kde klíčem je indexová hodnota, a hodnotou je seznam řádků, které odpovídají dané indexové hodnotě.
	/// Indexová hodnota je souhrn několika údajů z datového záznamu, vybíraný aplikačním kódem, typicky jeden nebo několik položek.
	/// Indexová hodnota je vyjádřena instancí konkrétní třídy, která je potomkem abstraktní třídy IndexItemCls, kde generikem je třída R (datový záznam).
	/// Třída indexové hodnoty je generikem K (Key).
	/// Datový záznam je generikum R (Record), obsahuje sadu proměnných, z nichž některé (jedna nebo více) tvoří index.
	/// Výsledný index obsahuje tolik záznamů, kolik je různých indexových hodnot.
	/// Pokud tedy vstupní data obsahují více záznamů, které vytvářejí stejnou indexovou hodnotu (Equals), pak jsou vedeny v jednom záznamu indexu.
	/// Strana Value v indexové Dictionary obsahuje List, jehož prvky jsou int, a obsahují klíče nebo čísla řádků, na nichž se vyskytuje daná indexová hodnota.
	/// Každá položka Dictionary tedy obsahuje jak Key = indexový klíč, a jako Value obsahuje List odkazů na data.
	/// </summary>
	/// <typeparam name="K">Třída, deklarující klíč indexu. Tato třída musí být potomkem generické třídy IndexItemCls, kde generikem je třída datového záznamu. Musí mít bezparametrický konstruktor.</typeparam>
	/// <typeparam name="R">Třída datových záznamů, z nichž se odvozují indexové klíče</typeparam>
	public class IndexDictCls<K, R> : IIndexDict<R> where K : IndexItemCls<R>, new()
	{
		#region INSTANČNÍ KONSTRUKCE
		/// <summary>
		/// Default konstruktor
		/// </summary>
		public IndexDictCls(IndexType indexType)
		{
			switch (indexType)
			{
				case IndexType.MultiRows:
					_IndexMulti = new Dictionary<K, List<int>>();
					_IndexUnique = null;
					_CurrentIndexType = IndexType.MultiRows;
					break;
				case IndexType.Unique:
					_IndexMulti = null;
					_IndexUnique = new Dictionary<K, int>();
					_CurrentIndexType = IndexType.Unique;
					break;
				default:
					_CurrentIndexType = 0;
					break;
			}
		}
		/// <summary>
		/// Konstruktor indexu na základě existujících dat ve formě List/Dictionary a se zadáním typu indexu (MultiRows/Unique).
		/// </summary>
		/// <param name="list">Existující data. Index řádku s daty bude použit jako Value hodnota indexu. Key hodnota indexu bude vytvářena typovým parametrem K.</param>
		/// <param name="indexType">Typ indexu. Pozor: Unique index musí mít Unique hodnoty, jinak vyhodí chybu při tvorbě. MultiRows index nepovolí použít funkci Seek, více viz tam.</param>
		public IndexDictCls(List<R> list, IndexType indexType)
		{
			switch (indexType)
			{
				case IndexType.MultiRows:
					_IndexMulti = GetIndexMultiRows(list);
					_IndexUnique = null;
					_CurrentIndexType = IndexType.MultiRows;
					break;
				case IndexType.Unique:
					_IndexMulti = null;
					_IndexUnique = GetIndexUnique(list);
					_CurrentIndexType = IndexType.Unique;
					break;
				default:
					_CurrentIndexType = 0;
					break;
			}
		}
		/// <summary>
		/// Konstruktor indexu na základě existujících dat ve formě List/Dictionary a se zadáním typu indexu (MultiRows/Unique).
		/// </summary>
		/// <param name="dict">Existující data. Key z z předané Dictionary bude použita jako Value hodnota indexu. Key hodnota indexu bude vytvářena typovým parametrem K.</param>
		/// <param name="indexType">Typ indexu. Pozor: Unique index musí mít Unique hodnoty, jinak vyhodí chybu při tvorbě. MultiRows index nepovolí použít funkci Seek, více viz tam.</param>
		public IndexDictCls(Dictionary<int, R> dict, IndexType indexType)
		{
			switch (indexType)
			{
				case IndexType.MultiRows:
					_CurrentIndexType = IndexType.MultiRows;
					_IndexMulti = GetIndexMultiRows(dict);
					_IndexUnique = null;
					break;
				case IndexType.Unique:
					_CurrentIndexType = IndexType.Unique;
					_IndexMulti = null;
					_IndexUnique = GetIndexUnique(dict);
					break;
			}
		}
        /// <summary>
        /// Konstruktor indexu na základě existujících dat ve formě List/Dictionary a se zadáním typu indexu (MultiRows/Unique).
        /// </summary>
        /// <param name="dict">Existující data. Key z z předané Dictionary bude použita jako Value hodnota indexu. Key hodnota indexu bude vytvářena typovým parametrem K.</param>
        /// <param name="indexType">Typ indexu. Pozor: Unique index musí mít Unique hodnoty, jinak vyhodí chybu při tvorbě. MultiRows index nepovolí použít funkci Seek, více viz tam.</param>
        public IndexDictCls(Dictionary<int, R> dict, IndexType indexType, Action<R, R> duplicityResolver)
        {
            switch (indexType)
            {
                case IndexType.MultiRows:
                    _CurrentIndexType = IndexType.MultiRows;
                    _IndexMulti = GetIndexMultiRows(dict);
                    _IndexUnique = null;
                    break;
                case IndexType.Unique:
                    _CurrentIndexType = IndexType.Unique;
                    _IndexMulti = null;
                    _IndexUnique = GetIndexUnique(dict, duplicityResolver);
                    break;
            }
        }
		/// <summary>
		/// Typ tohoto indexu (MultiRows nebo Unique). Je definován v konstruktoru. Podle typu indexu je třeba s ním zacházet.
		/// Více viz hodnoty IndexType.
		/// </summary>
		public IndexType CurrentIndexType { get { return _CurrentIndexType; } }
		private IndexType _CurrentIndexType;
		/// <summary>
		/// Počet klíčů v tomto indexů. Nemusí odpovídat počtu indexovaných vět, pokud jde o index typu MultiRows.
		/// Pokud není objekt inicializován, vrací -1.
		/// </summary>
		public int Count
		{
			get
			{
				switch (_CurrentIndexType)
				{
					case IndexType.MultiRows:
						if (_IndexMulti != null)
							return _IndexMulti.Count;
						break;
					case IndexType.Unique:
						if (_IndexUnique != null)
							return _IndexUnique.Count;
						break;
				}
				return -1;
			}
		}
		/// <summary>
		/// Unique index: pro jeden klíč lze evidovat nejvýš jednu hodnotu. 
		/// Pokus o založení jiné hodnoty pod existující klíč způsobí výjimku.
		/// </summary>
		private Dictionary<K, int> _IndexUnique;
		/// <summary>
		/// MultiRows index: pro jeden klíč eviduje seznam hodnot.
		/// Pokus o čtení Seek() způsobí výjimku (i když by pro hledaný klíč existovala jen jedna hodnota, protože to je věcí náhody, 
		/// ale volání Seek na indexu MultiRows je chybou samo o sobě. Má se použít metoda SeekFirst().
		/// </summary>
		private Dictionary<K, List<int>> _IndexMulti;
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Index " + this._CurrentIndexType.ToString() + "; KeyCount=" + Count.ToString() + "}";
		}
		#endregion
		#region SLUŽBY INDEXU
		/// <summary>
		/// Zjistí, zda pro daný klíč existuje položka s daty.
		/// Vrací true = existuje, false = neexistuje.
		/// </summary>
		/// <param name="key">Klíčová data věty</param>
		/// <returns>true = položka s daty existuje, false = neexistuje</returns>
		public bool ContainsKey(K key)
		{
			switch (this.CurrentIndexType)
			{
				case IndexType.Unique:
					return _IndexUnique.ContainsKey(key);
				case IndexType.MultiRows:
					return _IndexMulti.ContainsKey(key);
			}
			return false;
		}
		/// <summary>
		/// V jednom chodu zjistí, zda existuje položka s daty, vrací příznak její existence, a do out parametru vloží její hodnotu.
		/// Vrací true = existuje, false = neexistuje.
		/// </summary>
		/// <param name="key">Klíčová data věty</param>
		/// <returns>true = položka s daty existuje, false = neexistuje</returns>
		public bool TryGetValue(K key, out int record)
		{
			if (this.CurrentIndexType != IndexType.Unique)
				Throw.SysError(MessageInfo.Get("Metodu Index.TryGetValue(K key, out int record) lze použít pouze pro index typu Unique. Aktuální index je typu %0. Zde je nutno použít metodu Index.TryGetValue(K key, out List<int> recordList).", this.CurrentIndexType.ToString()));

			return _IndexUnique.TryGetValue(key, out record);
		}
		/// <summary>
		/// V jednom chodu zjistí, zda existuje položka s daty, vrací příznak její existence, a do out parametru vloží soupis odpovídajících vět.
		/// Pokud položka pro daný klíč neexistuje, do out parametru vloží null, což je jiné než prázdný list.
		/// Vrací true = existuje, false = neexistuje.
		/// </summary>
		/// <param name="key">Klíčová data věty</param>
		/// <returns>true = položka s daty existuje, false = neexistuje</returns>
		public bool TryGetValue(K key, out List<int> recordList)
		{
			if (this.CurrentIndexType != IndexType.MultiRows)
				Throw.SysError(MessageInfo.Get("Metodu Index.TryGetValue(K key, out List<int> recordList) lze použít pouze pro index typu MultiRows. Aktuální index je typu %0. Zde je nutno použít metodu Index.TryGetValue(K key, out int record).", this.CurrentIndexType.ToString()));

			recordList = null;
			List<int> data;
			bool find = _IndexMulti.TryGetValue(key, out data);
			if (find)
				recordList = new List<int>(data);
			return find;
		}
		/// <summary>
		/// Vyhledá index (typicky číslo záznamu, klíč do datového indexu) konkrétní jedné věty na základě klíčových dat.
		/// Lze použít pouze pro Unique indexy.
		/// Pokud pro index nenajde žádná data, vrátí 0.
		/// </summary>
		/// <param name="key">Klíčová data věty</param>
		/// <returns>Index odpovídajícího záznamu (číslo záznamu), nebo 0</returns>
		public int? Seek(K key)
		{
			if (this.CurrentIndexType != IndexType.Unique)
				Throw.SysError(MessageInfo.Get("Metodu Index.Seek() lze použít pouze pro index typu Unique. Aktuální index je typu %0. Zde je nutno použít metodu SeekFirst() nebo SeekAll().", this.CurrentIndexType.ToString()));

			int rec = 0;
			if (_IndexUnique.TryGetValue(key, out rec))
				return rec;
			return null;
		}
		/// <summary>
		/// Vyhledá index (typicky číslo záznamu, klíč do datového indexu) první věty, která odpovídá klíčovému záznamu.
		/// Lze použít pro MultiRows i pro Unique indexy.
		/// V případě MultiRows indexu a výskytu více odpovídajících vět vrátí index první věty.
		/// Pokud pro index nenajde žádná data, vrátí 0.
		/// </summary>
		/// <param name="key">Klíčová data věty</param>
		/// <returns>Index odpovídajícího záznamu (číslo záznamu), nebo 0</returns>
		public int? SeekFirst(K key)
		{
			int rec = 0;
			switch (this.CurrentIndexType)
			{
				case IndexType.Unique:
					if (_IndexUnique.TryGetValue(key, out rec))
						return rec;
					break;
				case IndexType.MultiRows:
					List<int> records;
					if (_IndexMulti.TryGetValue(key, out records))
					{
						if (records.Count >= 1)
							return records[0];
					}
					break;
			}
			return null;
		}
		/// <summary>
		/// Vyhledá indexy (typicky číslo záznamu, klíč do datového indexu) všech vět, které odpovídají klíčovému záznamu.
		/// Lze použít pro MultiRows i pro Unique indexy.
		/// V případě Unique indexu vrátí List s jedním prvkem, pokud byl nalezen.
		/// V případě MultiRows indexu vrátí List obsahující všechny odpovídající indexy.
		/// Pokud nenajde ani jeden záznam, vrátí null (nebudu vytvářet objekt, který nebude nic obsahovat).
		/// </summary>
		/// <param name="key">Klíčová data věty</param>
		/// <returns>Seznam indexů odpovídajících záznamů, nebo null</returns>
		public List<int> SeekAll(K key)
		{
			List<int> result = null;
			switch (this.CurrentIndexType)
			{
				case IndexType.Unique:
					int rec = 0;
					if (_IndexUnique.TryGetValue(key, out rec))
					{
						result = new List<int>();
						result.Add(rec);
					}
					break;
				case IndexType.MultiRows:
					List<int> records;
					if (_IndexMulti.TryGetValue(key, out records))
					{
						result = new List<int>(records);
					}
					break;
			}

			return result;
		}
		/// <summary>
		/// Do indexu přidá nový záznam. Pokud by tam takový již byl, chybu nevyvolá a duplicitní záznam nepřidá.
		/// </summary>
		/// <param name="key">Jeho číslo (record number v Dictionary nebo index v Listu</param>
		/// <param name="record">Celý záznam s daty, Indexový výraz si vytvoří index sám.</param>
		public void Add(int key, R record)
		{
			switch (this.CurrentIndexType)
			{
				case IndexType.Unique:
					_AddIndexUnique(record, key, this._IndexUnique, null, null);     // Zařadit do indexu: index key, odkaz na datový řádek, výsledný index
					break;
				case IndexType.MultiRows:
					_AddIndexMultiRows(record, key, this._IndexMulti);   // Zařadit do indexu: index key, odkaz na datový řádek, výsledný index
					break;
			}
		}
        /// <summary>
        /// Do indexu přidá nový záznam. Pokud by tam takový již byl, chybu nevyvolá a duplicitní záznam nepřidá.
        /// </summary>
        /// <param name="key">Jeho číslo (record number v Dictionary nebo index v Listu</param>
        /// <param name="record">Celý záznam s daty, Indexový výraz si vytvoří index sám.</param>
        public void Add(int key, K keyValue)
        {
            switch (this.CurrentIndexType)
            {
                case IndexType.Unique:
                    _AddIndexUnique(keyValue, key, this._IndexUnique);     // Zařadit do indexu: index key, odkaz na datový řádek, výsledný index
                    break;
                case IndexType.MultiRows:
                    _AddIndexMultiRows(keyValue, key, this._IndexMulti);   // Zařadit do indexu: index key, odkaz na datový řádek, výsledný index
                    break;
            }
        }
        /// <summary>
		/// Smaže svoje data
		/// </summary>
		public void Clear()
		{
			switch (this.CurrentIndexType)
			{
				case IndexType.Unique:
					_IndexUnique.Clear();
					break;
				case IndexType.MultiRows:
					_IndexMulti.Clear();
					break;
			}
		}
		#endregion
		#region STATIC GENERÁTORY INDEXŮ MultiRows i Unique
		/// <summary>
		/// Vygeneruje a vrátí index typu MultiRows pro daný seznam (List) prvků.
		/// </summary>
		/// <param name="list"></param>
		/// <returns></returns>
		public static Dictionary<K, List<int>> GetIndexMultiRows(List<R> list)
		{
			// Výsledný index
			Dictionary<K, List<int>> result = new Dictionary<K, List<int>>();
			for (int r = 0; r < list.Count; r++)
			{	// Pro každý vstupní datový záznam:
				R input = list[r];                  // Vstupní záznam, načíst ze vstupního pole
				_AddIndexMultiRows(input, r, result);        // Zařadit do indexu: index key, odkaz na datový řádek, výsledný index
			}
			return result;
		}
		/// <summary>
		/// Vygeneruje a vrátí index typu MultiRows pro daný slovník (Dictionary) prvků.
		/// </summary>
		/// <param name="dict"></param>
		/// <returns></returns>
		public static Dictionary<K, List<int>> GetIndexMultiRows(Dictionary<int, R> dict)
		{
			// Výsledný index
			Dictionary<K, List<int>> result = new Dictionary<K, List<int>>();
			foreach (KeyValuePair<int, R> kvp in dict)
				// Pro každý vstupní datový záznam:
				_AddIndexMultiRows(kvp.Value, kvp.Key, result);        // Zařadit do indexu: index key, odkaz na datový řádek, výsledný index

			return result;
		}
		/// <summary>
		/// Do vznikajícího indexu typu MultiRows přidá další položku.
		/// </summary>
		/// <param name="input">Vstupní datová věta</param>
		/// <param name="key">Odkaz na datovou větu (číslo řádku nebo klíč)</param>
		/// <param name="result">Indexový seznam</param>
		private static void _AddIndexMultiRows(R input, int key, Dictionary<K, List<int>> result)
		{
			K item = new K();                       // Vytvoříme indexovou hodnotu (prázdnou, pro generikum z abstraktní třídy to neumím jinak)
			bool add = item.FillFrom(input);        //  a indexovou hodnotu naplním daty z datového záznamu
			if (!add) return;                       // Tento index si nepřejeme přidávat.
			List<int> recList;
			if (!result.TryGetValue(item, out recList))
			{	// Pokud pro indexovou hodnotu zatím nemám záznam v indexu:
				recList = new List<int>();          // Vytvořím nový soupis odkazů na datové řádky a vložím soupis do indexu:
				result.Add(item, recList);
			}
			if (!recList.Contains(key))             // Odkaz na datový řádek zatím není v soupisu => přidám jej, proměnná recList je referencí do indexu.
				recList.Add(key);
		}
        /// <summary>
        /// Do vznikajícího indexu typu MultiRows přidá další položku.
        /// </summary>
        /// <param name="input">Vstupní datová věta</param>
        /// <param name="key">Odkaz na datovou větu (číslo řádku nebo klíč)</param>
        /// <param name="result">Indexový seznam</param>
        private static void _AddIndexMultiRows(K keyValue, int key, Dictionary<K, List<int>> result)
        {
            List<int> recList;
            if (!result.TryGetValue(keyValue, out recList))
            {	// Pokud pro indexovou hodnotu zatím nemám záznam v indexu:
                recList = new List<int>();          // Vytvořím nový soupis odkazů na datové řádky a vložím soupis do indexu:
                result.Add(keyValue, recList);
            }
            if (!recList.Contains(key))             // Odkaz na datový řádek zatím není v soupisu => přidám jej, proměnná recList je referencí do indexu.
                recList.Add(key);
        }
        /// <summary>
		/// Vygeneruje a vrátí index typu Unique pro daný seznam (List) prvků.
		/// </summary>
		/// <param name="list"></param>
		/// <returns></returns>
		public static Dictionary<K, int> GetIndexUnique(List<R> list)
		{
            return GetIndexUnique(list, null);
        }
        /// <summary>
        /// Vygeneruje a vrátí index typu Unique pro daný seznam (List) prvků.
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public static Dictionary<K, int> GetIndexUnique(List<R> list, Action<R, R> duplicityResolver)
        {
			// Výsledný index
			Dictionary<K, int> result = new Dictionary<K, int>();
			for (int r = 0; r < list.Count; r++)
			{	// Pro každý vstupní datový záznam:
				R input = list[r];                  // Vstupní záznam, načíst ze vstupního pole
				_AddIndexUnique(input, r, result, rec => list[rec], duplicityResolver);        // Zařadit do indexu: index key, odkaz na datový řádek, výsledný index
			}
			return result;
		}
		/// <summary>
		/// Vygeneruje a vrátí index typu Unique pro daný slovník (Dictionary) prvků.
		/// </summary>
		/// <param name="dict"></param>
		/// <returns></returns>
        public static Dictionary<K, int> GetIndexUnique(Dictionary<int, R> dict)
        {
            return GetIndexUnique(dict, null);
        }
		/// <summary>
		/// Vygeneruje a vrátí index typu Unique pro daný slovník (Dictionary) prvků.
		/// </summary>
		/// <param name="dict"></param>
		/// <returns></returns>
		public static Dictionary<K, int> GetIndexUnique(Dictionary<int, R> dict, Action<R, R> duplicityResolver)
		{
			// Výsledný index
			Dictionary<K, int> result = new Dictionary<K, int>();
			foreach (KeyValuePair<int, R> kvp in dict)
			{	// Pro každý vstupní datový záznam:
				_AddIndexUnique(kvp.Value, kvp.Key, result, rec => dict[rec], duplicityResolver);        // Zařadit do indexu: index key, odkaz na datový řádek, výsledný index
			}
			return result;
		}
		/// <summary>
		/// Do vznikajícího indexu typu Unique přidá další položku.
		/// </summary>
		/// <param name="dataNew">Vstupní datová věta</param>
		/// <param name="recNew">Odkaz na datovou větu (číslo řádku nebo klíč)</param>
		/// <param name="result">Indexový seznam</param>
        /// <param name="duplicityResolver">Metoda, která má řešit problémy s duplicitou</param>
		private static void _AddIndexUnique(R dataNew, int recNew, Dictionary<K, int> result, Func<int, R> searcher, Action<R, R> duplicityResolver)
		{
			K keyValue = new K();                        // Vytvoříme indexovou hodnotu (prázdnou, pro generikum z abstraktní třídy to neumím jinak)
			bool add = keyValue.FillFrom(dataNew);       //  a indexovou hodnotu naplním daty z datového záznamu
			if (!add) return;                            // Tento index si nepřejeme přidávat.

			int recOld;
			if (!result.TryGetValue(keyValue, out recOld))
			{	// Pokud pro indexovou hodnotu zatím nemám záznam v indexu:
				result.Add(keyValue, recNew);
			}
            else if (recOld != recNew)
            {   // Pro indexovou hodnotu (K item) máme v indexu záznam, ale s jiným Int32 klíčem => jde o jiný záznam se stejným klíčem => duplicita:
                if (recOld == 0)
                {   // Pokud jsem dosud držel hodnotu == 0, pak namísto ni vložím novou hodnotu bez problému:
                    result[keyValue] = recNew;
                }
                else
                {   // Minulá hodnota nebyla nula, jde o konflikt:
                    if (duplicityResolver != null)
                    {   // Existuje metoda, která řeší duplicity po svém (DAJ 0042587)
                        R dataOld = default(R);
                        try
                        {
                            dataOld = searcher(recOld);        // Dodaná metoda mi najde datový záznam, který už je uložen v indexu, a s nímž jsme v konfliktu. Přitom může teoreticky dojít k chybě = záznam neexistuje (??? !), s čímž si musí poradit resolver.
                        }
                        catch (Exception exc)
                        {
                            dataOld = default(R);
                        }

                        duplicityResolver(dataOld, dataNew);   // Uživatelem (tj. ten, kdo staví index) dodaná metoda, která tvrdí že umí řešit duplicity. Dostane stávající záznam s daty, a nový záznam který je duplicitní.
                    }
                    else
                        // Implicitní řešení duplicit = hození chyby:
                        // Záznam se shodným klíčem již máme v evidenci. Pokud má jiný klíč, jde o chybu Unique a budeme ji hlásit.
                        Throw.AplError(MessageInfo.Get("Porušení Unique hodnoty klíče v indexu: pro záznam %0 (klíč %1) je v indexu evidován jiný záznam s klíčem %2. Typ dat: %3, typ klíče: %4.", dataNew.ToString(), recNew, recOld, dataNew.GetType().Name, typeof(K).Name));
                }
            }
		}
        /// <summary>
        /// Do vznikajícího indexu typu Unique přidá další položku.
        /// </summary>
        /// <param name="dataNew">Vstupní datová věta</param>
        /// <param name="recNew">Odkaz na datovou větu (číslo řádku nebo klíč)</param>
        /// <param name="result">Indexový seznam</param>
        private static void _AddIndexUnique(K keyValue, int recNew, Dictionary<K, int> result)
        {
            int recOld;
            if (!result.TryGetValue(keyValue, out recOld))
            {	// Pokud pro indexovou hodnotu zatím nemám záznam v indexu:
                result.Add(keyValue, recNew);
            }
            else if (recOld != recNew)
            {   // Pro indexovou hodnotu (K item) máme v indexu záznam, ale s jiným Int32 klíčem => jde o jiný záznam se stejným klíčem => duplicita:
                // Záznam se shodným klíčem již máme v evidenci. Pokud má jiný klíč, jde o chybu Unique a budeme ji hlásit.
                Throw.AplError(MessageInfo.Get("Porušení Unique hodnoty klíče v indexu: pro záznam (klíč %0) je v indexu evidován jiný záznam s klíčem %1. Typ dat: %2, typ klíče: %3.", recNew, recOld, typeof(R).Name, typeof(K).Name));
            }
        }
        #endregion
	}
	/// <summary>
	/// Druhy indexů
	/// </summary>
	public enum IndexType
	{
		/// <summary>
		/// Víceřádkový index: umožní indexovat více datových záznamů, které mají shodnou indexovou hodnotu.
		/// Pak pro jednu indexovou hodnotu je v indexu evidováno více řádků s daty.
		/// Pro index tohoto typu není možno použít funkci Seek() (mude vyvolána výjimka), neboť funkce Seek vrací hodnotu Int32 (jednu hodnotu), 
		/// a u indexu MultiRows je typicky vracen List hodnoti Int32.
		/// Je možno použít funkci SeekFirst(), která vrátí první evidovaný Int32 (vrátí prvek 0 z Listu).
		/// </summary>
		MultiRows = 1,
		/// <summary>
		/// Jednořádkový index: pro jednu klíčovou hodnotu může evidovat pouze jeden datový záznam.
		/// Lze používat i metody určené pro MultiRows, budou vracet jednu hodnotu pro evidovaný záznam (nebo žádnou hodnotu, pokud pro klíč nic neevidujeme).
		/// Pokud pro index Unique budeme přidávat věty s duplicitním klíčem, bude vyvolána výjimka (porušení unikátnosti klíčů).
		/// </summary>
		Unique
	}
	/// <summary>
	/// Třída abstraktního indexu pro tvorbu indexu podle jednoho int sloupce
	/// </summary>
	public abstract class IndexInt32Cls<R> : IndexItemCls<R>
	{
		protected int Value;
		public override int GetHashCode()
		{
			return this.Value;
		}
		public override bool Equals(object obj)
		{
			if (obj is IndexInt32Cls<R>)
				return (this.Value == ((IndexInt32Cls<R>)obj).Value);
			return false;
		}
		public override string ToString()
		{
			return "{Key=" + this.Value.ToString() + "}";
		}
	}
	/// <summary>
	/// Abstraktní předek indexových tříd.
	/// Předepisuje metodu FillFrom(), která do instance této třídy přenese klíčové hodnoty z datové věty.
	/// Indexová třída musí implementovat korektně i metody override int GetHashCode() a override bool Equals()
	/// </summary>
	/// <typeparam name="R">Třída datové věty</typeparam>
	public abstract class IndexItemCls<R>
	{
		/// <summary>
		/// Povinná metoda indexu, která do své instance načte data z podkladové datové třídy.
		/// Využívá se při tvorbě indexu ve třídě IndexDictCls.
		/// Metoda má návratovou hodnotu, která umožňuje generovat filtrovaný index:
		/// pokud metoda vrátí true, bude daný záznam zařazen do indexu. Hodnota false zajistí, že se daná věta do indexu nedostane.
		/// </summary>
		/// <param name="record">Vstupní data, z nichž s emají opsat potřebné hodnoty do this</param>
		/// <returns>true = zařadit do indexu  /  false = nezařazovat do indexu</returns>
		public abstract bool FillFrom(R record);
	}
	/// <summary>
	/// Interface, který umožňuje pracovat s indexem bez znalosti datového typu jeho klíče, jen se znalostí typu dat.
	/// </summary>
	/// <typeparam name="R"></typeparam>
	public interface IIndexDict<R>
	{
		/// <summary>
		/// Do indexu přidá nový záznam. Pokud by tam atkový již byl, chybu nevyvolá a duplicitní záznam nepřidá.
		/// </summary>
		/// <param name="key">Jeho číslo (record number v Dictionary nebo index v Listu</param>
		/// <param name="record">Celý záznam s daty, Indexový výraz si vytvoří index sám.</param>
		void Add(int key, R item);
	}
	#endregion
}
